package cn.xxstudy.blog.service

import cn.xxstudy.blog.bean.BlogQuery
import cn.xxstudy.blog.dao.BlogRepository
import cn.xxstudy.blog.entity.Blog
import cn.xxstudy.blog.entity.Category
import cn.xxstudy.blog.entity.Tags
import cn.xxstudy.blog.util.getNullPropertyNames
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.*
import javax.persistence.criteria.*
import kotlin.collections.HashMap

/**
 * 作者：wuxinxi on 2020/11/25
 * 包名：cn.xxstudy.blog.service
 * TODO:博客查询服务类
 */
@Service
class BlogService : IBlogService {
    @Autowired
    lateinit var blogRepository: BlogRepository

    /**
     * 博客管理列表页
     */
    override fun listBlog(isAdmin: Boolean, pageable: Pageable, query: BlogQuery): Page<Blog> {
        return blogRepository.findAll({ root, cq, cb ->
            val predicates = mutableListOf<Predicate>()
            query.title?.let {
                //标题模糊查询
                predicates.add(cb.like(root.get("title"), "%${query.title}%"))
            }
            query.categoryId?.let {
                //分类Id
                predicates.add(cb.equal(root.get<Category>("category").get<Category>("id"), query.categoryId))
            }
            //推荐
            if (query.recommend) {
                predicates.add(cb.equal(root.get<Boolean>("recommendChecked"), query.recommend))
            }

            //勾选了草稿
            if (query.published) {
                predicates.add(cb.equal(root.get<Boolean>("published"), !query.published))
            }
            if (!isAdmin) {
                //非管理员登录时只允许查找已发布的博客
                predicates.add(cb.equal(root.get<Boolean>("published"), true))
            }
            cq.where(*predicates.toTypedArray())
            null
        }, pageable)
    }

    /**
     * 首页index -> 仅查询已发布的
     */
    override fun listBlog(pageable: Pageable): Page<Blog> {
        return blogRepository.findAll({ root, cq, cb ->
            cq.where(cb.equal(root.get<Boolean>("published"), true))
            null
        }, pageable)
    }

    override fun listBlog(tagId: Long, pageable: Pageable): Page<Blog> {
        return blogRepository.findAll({ root, cq, cb ->
            val join = root.join<Blog, Tags>("tags")
            val predicates = mutableListOf<Predicate>()
            predicates.add(cb.equal(root.get<Boolean>("published"), true))
            predicates.add(cb.equal(join.get<Tags>("id"), tagId))

//            return@findAll cb.equal(join.get<Tags>("id"), tagId)
            return@findAll cq.where(cb.equal(join.get<Tags>("id"), tagId),
                    cb.equal(root.get<Boolean>("published"), true)).restriction
        }, pageable)
    }

    override fun listBlog(query: String, pageable: Pageable): Page<Blog> = blogRepository.findByQuery(query, pageable)

    /**
     * 热门推荐：根据浏览数
     */
    override fun listRecommendBlogTop(size: Int): List<Blog> {
        val sort = Sort.by(Sort.Direction.DESC, "lookCount")
        val pageable = PageRequest.of(0, size, sort)
        return blogRepository.findTop(pageable)
    }

    override fun listNewBlogTop(size: Int): List<Blog> {
        val sort = Sort.by(Sort.Direction.DESC, "updateDateTime")
        val pageable = PageRequest.of(0, size, sort)
        return blogRepository.findTop(pageable)
    }


    override fun getBlog(id: Long): Blog? {
        return getBlog(false, id)
    }

    override fun getBlog(isAdmin: Boolean, id: Long): Blog? {
        val blog = blogRepository.getOne(id)
        if (isAdmin) {
            return blog
        }
        if (!blog.isPublished) {
            return null
        }
        return blog
    }

    @Transactional
    override fun getFormatBlog(id: Long): Blog? {
        val blog = getBlog(id)
        blog?.let {
            val newBlog = Blog()
            BeanUtils.copyProperties(it, newBlog)
            newBlog.content = newBlog.content
            blogRepository.updateLookCount(id)
            return newBlog
        }
        return null
    }

    override fun historyMap(): Map<String, List<Blog>> {
        val yearList = blogRepository.findGroupYear()
        val map = HashMap<String, List<Blog>>()
        yearList.forEach {
            map[it] = blogRepository.findByYear(it)
        }
        return map
    }

    override fun blogCount(): Long {
        return blogRepository.count { root, query, criteriaBuilder ->
            query.where(criteriaBuilder.equal(root.get<Boolean>("published"), true))
            null
        }
    }

    @Transactional
    override fun saveBlog(blog: Blog): Blog? {
        return try {
            if (blog.id == null) {
                blogRepository.save(blog)
            } else {
                blog.updateDateTime = Date()
            }
            return blogRepository.save(blog)
        } catch (e: Exception) {
            null
        }
    }

    @Transactional
    override fun updateBlog(id: Long, blog: Blog): Blog? {
        return try {
            val repBlog = blogRepository.getOne(id)
            BeanUtils.copyProperties(blog, repBlog, *getNullPropertyNames(blog))
            repBlog.updateDateTime = Date()
            return blogRepository.save(repBlog)
        } catch (e: Exception) {
            null
        }
    }

    @Transactional
    override fun deleteBlog(id: Long): Boolean = blogRepository.deleteBlogById(id) > 0


}